package p49_事件发布器;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.event.GenericApplicationListener;
import org.springframework.core.ResolvableType;

import java.util.ArrayList;
import java.util.List;

/**
 * 自定义事件发布器,所有事件发布器都实现了ApplicationEventMulticaster
 * 发布器持有了广播器，使用广播器进行事件发布
 */
public class _自定义广播器 extends _广播器适配器 {

    /**
     * 作为事件发布器，肯定保存了所有监听器，这个list集合就用来保存监听器
     */
    List<GenericApplicationListener> list = new ArrayList<>();

    @Autowired
    private AnnotationConfigApplicationContext context;

    /**
     * 收集所有事件监听器，将来发布事件时，一个个去调用监听器的方法
     * spring容器初始化时，发现一个实现了ApplicationListener接口的bean，就回调一次该方法，bean名称作为方法参数
     * 只有ApplicationListener的实现类注册时才会回调该方法，加了注解的监听方法并不会，可能spring原始的监听器做了额外处理
     */
    @Override
    public void addApplicationListenerBean(String listenerBeanName) {
        ApplicationListener listener = context.getBean(listenerBeanName, ApplicationListener.class);

        //获取该监听器实现接口的泛型，表示该监听器对哪种事件感兴趣
        ResolvableType interestedType = ResolvableType.forClass(listener.getClass()).getInterfaces()[0].getGeneric();

        //将原始ApplicationListener包装成GenericApplicationListener
        GenericApplicationListener applicationListener = new GenericApplicationListener(){
            //判断事件真实类型与该监听器支持类型是否匹配，参数:真正发生的真实事件类型
            @Override
            public boolean supportsEventType(ResolvableType eventType) {
                return interestedType.isAssignableFrom(eventType);
            }

            //执行监听器方法(其实监听器方法是被间接调用)
            @Override
            public void onApplicationEvent(ApplicationEvent event) {
                //这里可以使用线程池来回调监听器方法
                listener.onApplicationEvent(event);
            }
        };
        list.add(applicationListener);
    }

    /**
     * 发布事件时(publisher.publishEvent())，就会间接调用到该方法
     * 根据事件类型从所有事件监听器里一个一个找，看哪个事件监听器感兴趣的事件类型是当前事件，就回调哪个事件监听器的onApplicationEvent方法
     * 参数：1事件真实对象 2事件真实类型(总是为null不知道为啥)
     */
    @Override
    public void multicastEvent(ApplicationEvent event, ResolvableType type) {
        //遍历集合，一个一个找合适的监听器
        for (GenericApplicationListener listener : list) {
            //判断遍历到的监听器是否对当前事件感兴趣
            if (listener.supportsEventType(ResolvableType.forClass(event.getClass()))) {
                listener.onApplicationEvent(event);
            }
        }
    }
}
